﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data;

namespace SqlCeDriver
{
	/*
		Created just to break up the large driver class. This will take care of all the 
		extended functions that don't necessarily generate Sql driven datasets.
	*/
	class ExtensionCommandProcessor
	{
		internal ExtensionCommandProcessor(SqlCeDataSource parent)
		{
			this.dataSource = parent;
		}

		/*
			command:		the command to process (duh). Likely to contain parameter values
			object window	preserving the farce that this could work on other than WINFORMS this is a parent object
							to host a dialog.
			connectionConfig param to give the operations access to other connections for eg. copy table and quick diff
			searchText		Filter box text
			out nextCommand A command to execute if this is necessary. See QuickDiff
			Return			true if we've done something to change the grid currently displayed.
		*/
		internal bool ProcessCommand(DbView.Command command, object window, DbViewDriver.IConnectionConfig connectionConfig, String searchText, out DbView.Command nextCommand)
		{
			nextCommand = new DbView.Command(DbView.Commands.Cmnd.Nothing);
			String connectionString = this.dataSource.ConnectionString;
			System.Windows.Forms.Form parent = window as System.Windows.Forms.Form;
			// for sql command api an error message and success flag
			String script = "";
			String execMessage;
			bool isOpOk = false;

			switch (command.Name)
			{
				case Cmd.CreateTable:
					{
						// context for the dialog
						using (CreateTableForm form = new CreateTableForm())
						{
							while (true) // keep displaying the dialog if there is an error
							{
								if (form.ShowDialog(parent) == System.Windows.Forms.DialogResult.OK)
								{
									bool createdOk;
									String result = new Helper(this.dataSource.GetAccessor()).GenericExecute(connectionString, form.Script, out createdOk);
									if (createdOk)
										return true;
									else
										System.Windows.Forms.MessageBox.Show(result + "\n" + form.Script, "Create table failed", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
								}
								else
									return false;
							}
						}
					}
				case "TEST":
					{
						if (parent != null)
							System.Windows.Forms.MessageBox.Show(parent, "Hi from test");
						return true; // try refresh
					}

				case Cmd.CopyTable:
					{
						if (parent != null)
						{
							CopyTableForm copyDialog = new CopyTableForm(connectionConfig, this.dataSource);
							copyDialog.SourceTable = command[DbView.Command.TableParameterName(0)];
							// set the last one
							if (this.holdCopyTargetDb.Length > 0) copyDialog.TargetDb = this.holdCopyTargetDb;
							if (copyDialog.ShowDialog(parent) == System.Windows.Forms.DialogResult.OK)
							{
								this.holdCopyTargetDb = copyDialog.TargetDb;
								return true;
							}
						}
					}
					break;

				//case Cmd.QuickDiffDialog:
				//{
				//    if (parent != null)
				//    {
				//        // caller is used to determine the object type to set in the drop down
				//        // it will be set depending on which object list was right-clicked on
				//        String caller = command["CALLER"];
				//        QuickDiffDialog Dlg = new QuickDiffDialog(connectionConfig, QuickDiffEngine.ObjectsTypes, caller);
				//        // set the compared database to the last one used (if any)
				//        if (this.m_HoldCopyTargetDb.Length > 0) Dlg.TargetDb = m_HoldCopyTargetDb;
				//        // This may be obsolete as there is no Main menu quick diff
				//        if (this.m_HoldObjectType.Length > 0 && caller.Length == 0) Dlg.ObjectType = m_HoldObjectType;
				//        // show dialog and proceed if OK
				//        if (Dlg.ShowDialog(parent) == System.Windows.Forms.DialogResult.OK)
				//        {
				//            m_HoldCopyTargetDb = Dlg.TargetDb;  // update held selections for next time
				//            m_HoldObjectType = Dlg.ObjectType;
				//            // do the compare in the Engine object. connectionConfig will resolve the 
				//            // connection tags to proper connection strings.
				//            QuickDiffEngine Engine = new QuickDiffEngine(connectionConfig);
				//            // change this to a String.
				//            Engine.Configure(Dlg.SourceDb, Dlg.TargetDb, Dlg.ObjectType);
				//            // The engine creates a result table which we then need to plug in to the
				//            // main form grid view. This is a new pattern for doing things, which has somewhat
				//            // bent the framework slightly. The (present) solution is to maintain a repository
				//            // of data-tables in a singleton and attach an ID to the command so the table follows
				//            // the command into history. This leaves a problem of when to clear up the tables as 
				//            // they are referenced by a singleton that stays alive. For now it's brushed under 
				//            // the carpet.
				//            // implement a table ID requester for this
				//            int tableId = DataTableRepository.Instance.SetTable(Engine.Execute(String.Format("Quick Diff: {0}", Dlg.ObjectType)));
				//            // create a general purpose command 
				//            DbView.Command diffCommand = Cmd.MakeCommand(Cmd.DisplayHeldTable);
				//            // add table Id so it is stored with the command
				//            diffCommand.AddParam(CmdParam.TableId, tableId.ToString());
				//            nextCommand = diffCommand;
				//        }
				//    }
				//}
				////break;

				case Cmd.ScriptClass:
					{
						String table = command[DbView.Command.TableParameterName(0)];
						// context for the dialog
						using (ClassBuilderForm form = new ClassBuilderForm(table, this.dataSource))
						{
							form.ShowDialog(parent);
						}
					}
					break;

				case Cmd.AddFK:
					{
						String table = command["TABLE"];
						String field = command[DbView.Command.TableParameterName(0)];
						ForeignKeyForm fkeyDialog = new ForeignKeyForm((this.dataSource as DbViewDriver.IDataSource), table, field);
						if (fkeyDialog.ShowDialog(parent) == System.Windows.Forms.DialogResult.OK)
							return true;
					}
					break;
				case Cmd.DelFK:
					{
						String table = command[DbView.Command.TableParameterName(0)];
						String fk = command[CmdParam.FkName]; // set in TableCommandProcessor.BuildTable()
						script = String.Format("alter table {0} drop constraint {1}", Helper.SafeTableName(table), fk);
						bool createdOk;
						String result = new Helper(this.dataSource.GetAccessor()).GenericExecute(connectionString, script, out createdOk);
						if (createdOk)
							return true;
						else
							System.Windows.Forms.MessageBox.Show(result + "\n" + script, "Remove FK failed", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
					}
					break;

				//                case Cmd.ResetAuto:
				//                {
				//                    // test whether or not the table has a auto-increment key
				//                    // find max as per script of data
				//                    String table = command["TABLE"];
				//                    // check the identity key
				//                    script =String.Format(@"
				//select     cols.name as [Field Name]
				// from sys.columns cols
				//where cols.object_id = OBJECT_ID('{0}') 
				//  and cols.is_identity = 1
				//", Helper.SafeTableName(table));
				//                    DataTable dt = Helper.GenericQuery(connectionString, script);
				//                    if (dt.Rows.Count == 0)
				//                    {
				//                        System.Windows.Forms.MessageBox.Show("Could not locate identity field in "+table+"\n"+script, "Reset failed", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
				//                        return false;
				//                    }
				//                    String column = dt.Rows[0][0].ToString();
				//                    script = String.Format(@"
				//set IDENTITY_INSERT {0} OFF;
				//declare @reseed int;
				//select @reseed = max({1}) from {0};
				//DBCC CHECKIDENT ({0}, RESEED, @reseed);", Helper.SafeTableName(table), Helper.SafeSqlField(column));
				//                    execMessage = Helper.GenericExecute(connectionString, script, out opOk);
				//                    if (opOk) return true;
				//                    System.Windows.Forms.MessageBox.Show(execMessage + "\n" +script, "Reset failed", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);

				//                }

				//				break;
			}

			if (command.Cmd == DbView.Commands.Cmnd.Droptable)
			{
				String table = command["TABLE"];
				String message = String.Format("Confirm that you want to remove table {0} from the database.", table);
				String messageCaption = "Drop table";
				String failMessage = "Remove table failed";
				script = String.Format("DROP TABLE {0};", Helper.SafeTableName(table));
				// TODO: add reset autoinc
				return this.ExecuteWithFeedback(connectionString, script, failMessage, message, messageCaption, parent);
			}

			if (command.Cmd == DbView.Commands.Cmnd.Cleartable)
			{
				String table = command["TABLE"];
				String message = String.Format("Confirm that you want to clear all data from table {0}.", table);
				String messageCaption = "Truncate table";
				String failMessage = "Truncate table failed";
				script = String.Format("delete from {0};", Helper.SafeTableName(table));
				// TODO: add reset autoinc
				bool success = this.ExecuteWithFeedback(connectionString, script, failMessage, message, messageCaption, parent);
				if (success)
					success = new Helper(this.dataSource.GetAccessor()).ResetAutoIdentity(connectionString, table);
				return success;
			}

			if (command.Cmd == DbView.Commands.Cmnd.RemoveColumn)
			{
				String table = command["TABLE"];
				String column = command[DbView.Command.TableParameterName(0)];
				String message = String.Format("Confirm that you want to remove column {1} from table {0}.", table, column);
				String messageCaption = "Remove column";
				String failMessage = "remove column table failed";
				script = String.Format("ALTER TABLE {0} DROP COLUMN {1};", Helper.SafeTableName(table), Helper.SafeSqlField(column));

				return this.ExecuteWithFeedback(connectionString, script, failMessage, message, messageCaption, parent);
			}
			if (command.Cmd == DbView.Commands.Cmnd.AddColumn)
			{
				String table = command["TABLE"];
				String column = command[DbView.Command.TableParameterName(0)];
				// get 
				// SqlTableSchema schema = this.m_Source.MakeSchemaFor(table);
				SqlTableSchema schema = Helper.MakeSchemaFor(table, this.dataSource.GetAccessor(), this.dataSource.ConnectionString);
				DbView.FieldInfo fieldInfo = ModifyColumnForm.NewField();
				ModifyColumnForm dialog = new ModifyColumnForm(table, schema, fieldInfo);
				while (true)
				{
					if (dialog.ShowDialog(parent) == System.Windows.Forms.DialogResult.Cancel)
						return false;

					script = dialog.GenerateScript(false);

					//String messageCaption = "Add Column";
					String failMessage = "Add column failed";
					if (this.ExecuteWithFeedback(connectionString, script, failMessage, "", "", parent))
						return true; // done ok so leave
					// here if execute errored. Redisplay dialog so user can correct or cancel.
				}
			}

			if (command.Cmd == DbView.Commands.Cmnd.ChangeColumn)
			{
				String table = command["TABLE"];
				String column = command[DbView.Command.TableParameterName(0)];
				// get 
				//SqlTableSchema schema = this.m_Source.MakeSchemaFor(table);
				SqlTableSchema schema = Helper.MakeSchemaFor(table, this.dataSource.GetAccessor(), this.dataSource.ConnectionString);
				DbView.FieldInfo fieldInfo = schema[column];

				if (!this.CheckColumnIsOkToModify(parent, fieldInfo)) // disallow PKey, index, fkey etc.
					return false;

				ModifyColumnForm dialog = new ModifyColumnForm(table, schema, fieldInfo);
				if (dialog.ShowDialog(parent) == System.Windows.Forms.DialogResult.Cancel)
					return false;

				script = dialog.GenerateScript(false);

				String failMessage = "Modify column failed";
				// return ExecuteWithFeedback(connectionString, script, failMessage, "", "", parent);
				execMessage = new Helper(this.dataSource.GetAccessor()).GenericExecute(connectionString, script, out isOpOk);
				if (isOpOk) 
					return true; 

				String altMessage = dialog.AltEffect();
				String message = String.Format("Could not modify column without side effects. " +
											   "The side effect is this:\n\n" +
											   "{0}\n\n" +
											   "Would you like to proceed with the change?"
											   , altMessage);
				if (System.Windows.Forms.MessageBox.Show(parent, message, "Problem with column change", System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.No)
					return false;
				// try for alt message
				script = dialog.GenerateScript(true);
				return this.ExecuteWithFeedback(connectionString, script, failMessage, "", "", parent);
			}
			return false;
		}

		private bool CheckColumnIsOkToModify(System.Windows.Forms.Form parent, DbView.FieldInfo fieldInfo)
		{
			String message = "";
			if (fieldInfo.IsPKey)
				message = "Cannot modify a primary key";
			else
				if (fieldInfo.IsFKey)
					message = "Cannot modify a foreign key column";
				else
					if (fieldInfo.IsIndexed)
						message = "Cannot modify a column in an index";
			if (message.Length == 0) return true;

			System.Windows.Forms.MessageBox.Show(parent, message, "Cannot modify column", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
			return false;
		}

		bool ExecuteWithFeedback(String connectionString, String script, String failMessage, String confirmMessage, String confirmTitle, System.Windows.Forms.Form parent)
		{
			bool proceed = true;
			if (confirmMessage.Length > 0)
				if (!this.ConfirmDangerousOperation(parent, confirmTitle, confirmMessage))
					proceed = false;
			if (proceed)
			{
				bool isOpOk;
				String execMessage = new Helper(this.dataSource.GetAccessor()).GenericExecute(connectionString, script, out isOpOk);
				if (isOpOk) return true;
				System.Windows.Forms.MessageBox.Show(parent, execMessage + "\n" + script, failMessage, System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
			}
			return false;
		}

		bool ConfirmDangerousOperation(System.Windows.Forms.Form parent, String operation, String message)
		{
			System.Windows.Forms.DialogResult answer = System.Windows.Forms.MessageBox.Show(parent, message, operation, System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Warning);
			return (answer == System.Windows.Forms.DialogResult.Yes) ? true : false;
		}

		SqlCeDataSource dataSource;
		/////////////////////////////////////////////////////////////////
		// state
		// use for both copy and compare options.
		String holdCopyTargetDb = "";
		//String m_HoldObjectType = "";
	}
}
